The VST 3 API is an interface collection designed for realtime audio processing components. Such a component can be an audio effect or an audio instrument.
VST 3 is based on a technology called VST Module Architecture. Please have a look at the VST-MA documentation to find out more about how the Plug-in system works in general.
The API files belonging to VST 3 are located in folder pluginterfaces/vst
A VST 3 audio effect or instrument basically consists of two parts: a processing part and an edit controller part.
The corresponding interfaces are:
The design of VST 3 suggests a complete separation of processor and edit controller by implementing two components. Splitting up an effect into these two parts requires some extra efforts for an implementation of course.
But this separation enables the host to run each component in a different context. It can even run them on different computers. Another benefit is that parameter changes can be separated when it comes to automation. While for processing these changes need to be transmitted in a sample accurate way, the GUI part can be updated with a much lower frequency and it can be shifted by the amount that results from any delay compensation or other processing offset.
A Plug-in that supports this separation has to set the Steinberg::Vst::kDistributable flag in the class info of the processor component (Steinberg::PClassInfo2::classFlags). Of course not every Plug-in can support this, for example if it depends deeply on resources that can not be easily moved to another computer. So when this flag is not set, the host must not try to separate the components in any way.
Although it is not recommended, it is possible to implement both, the processing part and the controller part in one component class. The host tries to query the Steinberg::Vst::IEditController interface after creating an Steinberg::Vst::IAudioProcessor and on success uses it as controller.
Both Steinberg::Vst::IComponent and Steinberg::Vst::IEditController derive from Steinberg::IPluginBase. The purpose of this basic interface is to initialize the component and to terminate it before it is destroyed.
The context parameter passed to Steinberg::IPluginBase::initialize is Steinberg::Vst::IHostApplication. Hosts should not call others functions before initialize is called!, except Steinberg::Vst::IComponent::setIoMode which need to be called before or Steinberg::Vst::IComponent::getControllerClassId which could be called before.
Here an example of host implementation creating the component and its associated controller of a Plug-in with a given classID:
//------------------------------------------------------------------------ ... Vst::IComponent* processorComponent; Vst::IEditController* editController; IPluginFactory* factory; ... // factory already initialized (after the library is loaded, see validator for example) ... // create its component part tresult result = factory->createInstance (classID, Vst::IComponent::iid, (void**)&processorComponent); if (processorComponent && (result == kResultOk)) { // initialize the component with our host context (note: initialize called just after creatInstance) res = (processorComponent->initialize (gStandardPluginContext) == kResultOk); // try to create the controller part from the component // for Plug-ins which did not succeed to separate component from controller :-( if (processorComponent->queryInterface (Vst::IEditController::iid, (void**)&editController) != kResultTrue) { FUID controllerCID; // ask for the associated controller class ID (could be called before processorComponent->initialize ()) if (processorComponent->getControllerClassId (controllerCID) == kResultTrue && controllerCID.isValid ()) { // create its controller part created from the factory result = factory->createInstance (controllerCID, Vst::IEditController::iid, (void**)&editController); if (editController && (result == kResultOk)) { // initialize the component with our context res = (editController->initialize (gStandardPluginContext) == kResultOk); // now processorComponent and editController are initialized... :-) } } } } //------------------------------------------------------------------------
The functionality of the components implementing these basic interfaces can be extended by a number of optional interfaces, that only need to be implemented if this extension is required.
The host stores and restores the complete state of the processor and of the controller in project files and in preset files:
Back to Contents
The processing part consists of two related interfaces. The reason for the split-up is the idea to use the basic interfaces Vst::IComponent not only for audio Plug-ins but also for other kinds of media as well (e.g. video processing in the future). Hence the Vst::IAudioProcessor interface represents the audio specific part of a processing component. Let's have a closer look at the concepts.
Steinberg::Vst::IComponent:
Steinberg::Vst::IAudioProcessor:
Back to Contents
The edit controller is responsible for the GUI aspects of the Plug-in. Its standard interface is Steinberg::Vst::IEditController. The host has to provide a callback interface for the edit controller named Steinberg::Vst::IComponentHandler. The handler is essential for the communication with both the host and the processor.
Back to Contents
The two VST 3 components (processor and controller) need a way to communicate. It is the task of the host to handle this.
All standard data (like parameter changes) are transmitted between processor and controller using the basic interfaces listed above.
Data that is unknown to the host can be transmitted by the means of messages. The communication interfaces are
Please note that messages from the processor to the controller must not be sent during the process call! This sending could be not speed enough and then break the real time processing. Such tasks should be handled in a separate timer thread.
Here an example of host implementation where the component and controller parts are connected and synchronized:
... // the component and the controller parts are previously be created and initialized (see above) ... if (editController) { // set the host handler // the host set its handler to the controller editController->setComponentHandler (myHostComponentHandler); // connect the 2 components Vst::IConnectionPoint* iConnectionPointComponent = 0; Vst::IConnectionPoint* iConnectionPointController = 0; processorComponent->queryInterface (Vst::IConnectionPoint::iid, (void**)&iConnectionPointComponent); editController->queryInterface (Vst::IConnectionPoint::iid, (void**)&iConnectionPointController); if (iConnectionPointComponent && iConnectionPointController) { iConnectionPointComponent->connect (iConnectionPointController); iConnectionPointController->connect (iConnectionPointComponent); } // synchronize controller to component by using setComponentState FMemoryStream stream; stream.setByteOrder (kLittleEndian); if (processorComponent->getState (&stream)) { stream.rewind (); editController->setComponentState (&stream); } // now processorComponent and editController parts are connected and synchronized...:-) }
Please note that you cannot rely on the implementation detail that the connection is done directly between the processor component and the edit controller.
Back to Contents